package com.dd.common.compoents;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.dd.common.response.ResultCode;
import com.dd.common.expection.BizException;
import com.dd.pojo.entry.File;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.SecretKey;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;


@Component
@RequiredArgsConstructor
@Slf4j
public class MinioTemplate {

    private final MinioClient minioClient;


    private final static String FILE_DEFAULR_BUCKETNAME="pandata";

    private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;

    /**
     * 验证bucketName是否存在
     * @param bucketName 桶名称
     * @return boolean true:存在
     */
    public boolean bucketExists(String bucketName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }



    /**
     * PUBLIC桶策略
     * 如果不配置，则新建的存储桶默认是PRIVATE，则存储桶文件会拒绝访问 Access Denied
     *
     * @param bucketName
     * @return
     */
    private static String publicBucketPolicy(String bucketName) {
        /**
         * AWS的S3存储桶策略
         * Principal: 生效用户对象
         * Resource:  指定存储桶
         * Action: 操作行为
         */
        StringBuilder builder = new StringBuilder();
        builder.append("{\"Version\":\"2012-10-17\","
                + "\"Statement\":[{\"Effect\":\"Allow\","
                + "\"Principal\":{\"AWS\":[\"*\"]},"
                + "\"Action\":[\"s3:ListBucketMultipartUploads\",\"s3:GetBucketLocation\",\"s3:ListBucket\"],"
                + "\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]},"
                + "{\"Effect\":\"Allow\"," + "\"Principal\":{\"AWS\":[\"*\"]},"
                + "\"Action\":[\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\"],"
                + "\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}");

        return builder.toString();
    }
    /**
     * 创建bucket
     *
     * @param bucketName bucket名称
     */
    public  void createBucket(String bucketName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException, RegionConflictException {
        BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
        if (!minioClient.bucketExists(bucketExistsArgs)) {
            MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();

            minioClient.makeBucket(makeBucketArgs);
            // 设置存储桶访问权限为PUBLIC， 如果不配置，则新建的存储桶默认是PRIVATE，则存储桶文件会拒绝访问 Access Denied
            SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs
                    .builder()
                    .bucket(bucketName)
                    .config(publicBucketPolicy(bucketName))
                    .build();
            minioClient.setBucketPolicy(setBucketPolicyArgs);
        }
    }

    /**
     * 根据bucketName删除信息
     *
     * @param bucketName bucket名称
     */
    public  void removeBucket(String bucketName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 拷贝文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param srcBucketName 目标bucket名称
     * @param srcObjectName 目标文件名称
     */
    public  ObjectWriteResponse copyObject(String bucketName, String objectName,
                                                 String srcBucketName, String srcObjectName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        return minioClient.copyObject(
                CopyObjectArgs.builder()
                        .source(CopySource.builder().bucket(bucketName).object(objectName).build())
                        .bucket(srcBucketName)
                        .object(srcObjectName)
                        .build());
    }

    /**
     * 判断文件是否存在
     *
     * @param bucketName 存储桶
     * @param objectName 对象
     * @return true：存在
     */
    public  boolean doesObjectExist(String bucketName, String objectName) {
        boolean exist = true;
        try {
            minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            exist = false;
        }
        return exist;
    }

    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     */
    public  void removeObject(String bucketName, String objectName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 获取文件流
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return 二进制流
     */
    public  InputStream getFileStream(String bucketName, String objectName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 上传普通文件
     * @param bucketName 桶名称
     * @param objectName 文件名称
     * @param file 文件
     * @param contentType 文件类型
     */
    public void  uploadFile(String bucketName, String objectName, MultipartFile file,String contentType) throws IOException, ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
            InputStream stream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName)
                    .object(objectName).stream(stream,stream.available(),-1)
                    .contentType(contentType).build());
    }


    /**
     * 上传分片
     * @param bucketName 桶名称
     * @param objectName 文件名称
     * @param file 文件

     */
    public void  uploadFile(String bucketName, String objectName, MultipartFile file) throws IOException, ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {



        InputStream stream = file.getInputStream();
        minioClient.putObject(PutObjectArgs.builder().bucket(bucketName)
                .object(objectName).stream(stream,stream.available(),-1)
                .build());
        stream.close();
    }

    public void upload(InputStream in,String index) throws IOException, ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        minioClient.putObject(PutObjectArgs.builder().bucket(FILE_DEFAULR_BUCKETNAME)
                .object("1991554845/"+index).stream(in,in.available(),-1)
                .build());
    }





    /**
     * 断点下载
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @param offset  开始位置
     * @param length 传送的长度
     * @return
     */
   public InputStream breakpointDownload(String bucketName, String objectName, Long offset, Long length) {
       InputStream stream = null;
       try {
           stream = minioClient.getObject(
                   GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length)
                           .build());
       } catch (Exception e) {
         throw  new BizException(ResultCode.FILE_POINT_DOWN_ERROR);
       }
       return stream;

   }


    /**
     * 创建文件夹

     * @param folderName 文件夹名称
     * @return
     */
   public boolean mkdirFolder(String folderName){

    try {
        minioClient.putObject(
                PutObjectArgs.builder().bucket(FILE_DEFAULR_BUCKETNAME).object(folderName+"/").stream(
                                new ByteArrayInputStream(new byte[] {}), 0, -1)
                        .build());

        return  true;
    }
    catch (Exception e){

        log.error("minio创建文件夹异常--{}",e.getMessage());

        return false;
    }

   }
    /**
     * 判断文件夹是否存在
     *
     * @param
     * @param objectName 文件夹名称（去掉/）
     * @return true：存在
     */
    public boolean doesFolderExist( String objectName) {
        boolean exist = false;
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(FILE_DEFAULR_BUCKETNAME).prefix(objectName).recursive(false).build());
            StringBuilder objectNameBuilder = new StringBuilder(objectName);
            for (Result<Item> result : results) {
                objectNameBuilder.append("/");
                Item item = result.get();
                if (item.isDir() && objectNameBuilder.toString().equals(item.objectName())) {
                    exist = true;
                }
            }
        } catch (Exception e) {
            exist = false;
        }
        return exist;
    }

    /**
     * 删除临时文件夹以及下面的文件
     * @param objectName
     */
    public void deleteObject( String objectName) {
        String objectNames = objectName + "/";
        try {
            if (StringUtils.isNotBlank(objectNames)) {
                if (objectNames.endsWith(".") || objectNames.endsWith("/")) {
                    Iterable<Result<Item>> list = minioClient.listObjects(ListObjectsArgs.builder().bucket(FILE_DEFAULR_BUCKETNAME).prefix(objectNames).recursive(false).build());
                    list.forEach(e -> {
                        try {
                            minioClient.removeObject(RemoveObjectArgs.builder().bucket(FILE_DEFAULR_BUCKETNAME).object(e.get().objectName()).build());
                        } catch (Exception a) {
                            return;
                        }

                    });
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    /**
     * 获取已经上传的分片数组--0-1-2-3
     * @param filePro 文件前缀
     * @return
     */
   public List<Integer>chunksNameList(String filePro){


         String objectName=filePro+"/";

        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(FILE_DEFAULR_BUCKETNAME).prefix(objectName).recursive(false).build());

        Iterator<Result<Item>> iterator = results.iterator();

        List<Integer>chunks=new ArrayList<>();

        while(iterator.hasNext()){
            try {
                Item item = iterator.next().get();
              //  System.out.println(item.objectName());
                String[] split = item.objectName().split("/");

                if(split.length>=2){
                    chunks.add(Integer.parseInt(split[1]));
                }
             //Arrays.stream(split).forEach(System.out::println);

//                System.out.println(split[0]);
//
//                System.out.println(split[1]);
              // chunks.add(Integer.parseInt(split[1]));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        Collections.sort(chunks);

        return chunks;
    }



    /**
     * 合并文件
     * @param folderName
     * @param objectName
     */
    public void merger(String folderName,String objectName)  {
       try{


           List<Integer> list = chunksNameList(folderName);
           List<String> files = new ArrayList<>();

           for(Integer name:list){
               files.add(folderName+"/"+name);
           }

           List<ComposeSource> sources = new ArrayList<>();
           for(String fileName:files){
               sources.add(ComposeSource.builder().bucket(FILE_DEFAULR_BUCKETNAME).object(fileName).build());
           }

           minioClient.composeObject(ComposeObjectArgs.builder()
                   .bucket(FILE_DEFAULR_BUCKETNAME).object(objectName).sources(sources).build());
       }
       catch (Exception e){
           log.error("合并分片文件异常--{}",e.getMessage());

       }
    }






    /**
     * 分享。
     *
     * @param objectName 存储桶里的对象名称
     * @param expires    失效时间（以秒为单位），默认是7天，不得大于七天
     * @return
     */
    public String getPresignedObjectUrl( String objectName, Integer expires) throws Exception {
        boolean flag = bucketExists(FILE_DEFAULR_BUCKETNAME);

        if (flag) {
            if (null == expires) {
                expires = DEFAULT_EXPIRY_TIME;
            }
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new RuntimeException("expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
            }
            try {
                return minioClient.getPresignedObjectUrl(Method.GET,FILE_DEFAULR_BUCKETNAME, objectName, expires, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }









}
